package com.gopay.common.trace.hessian;

import com.caucho.hessian.client.HessianProxy;
import com.caucho.hessian.client.HessianProxyFactory;
import com.caucho.hessian.client.LogHessianProxy;
import com.caucho.hessian.io.HessianRemoteObject;
import com.gopay.common.CustomHostnameVerifier;
import com.gopay.common.hessian.SendHessianKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Map;

/**
 * Custom Hessian代理工厂
 * 
 * @CustomHessianProxyFactory.java
 * @author MA HUAN
 * @author KZ-TU
 * @2013-1-17 上午11:43:35 www.gopay.com.cn Inc.All rights reserved.
 */
public class LogHessianProxyFactory extends HessianProxyFactory {

   // private static final Logger logger = LoggerFactory.getLogger(LogHessianProxyFactory.class);

    /**
     * connectTimeOut an int that specifies the timeout value to be used in
     * milliseconds
     */
    private int connectTimeOut = 0;
    /**
     * readTimeOut an int that specifies the timeout value to be used in
     * milliseconds
     */
    private int readTimeOut = 0;
    /**
     * Hostname verifier used when making an SSL request to the remote service.
     */
    private HostnameVerifier hostnameVerifier;

    public LogHessianProxyFactory() {
        super();
        // 在调用openConnection之前就会判断这些属性值，所以在构造方法里面进行初始化设置。
        setChunkedPost(false);
        setOverloadEnabled(true);
        this.hostnameVerifier = new CustomHostnameVerifier();
    }


    /**
     * 启动的时候 会调用该方法
     * @param api
     * @param urlName
     * @return
     * @throws MalformedURLException
     */
    @Override
    public Object create(Class api, String urlName) throws MalformedURLException {
        if (api == null) {
            throw new NullPointerException("api must not be null for HessianProxyFactory.create()");
        }
        InvocationHandler handler = null;

        URL url = new URL(urlName);

        //logger.info("api={}, urlName={}", new Object[]{api, urlName});

        ///自定义代理
        handler = new LogHessianProxy(this, url);

        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                new Class[] { api,
                        HessianRemoteObject.class },
                handler);
    }

    /**
     * 处理header
     * @param conn
     */
    private void handlHeader(URLConnection conn) {

        Map<String, String> headers = HessianHeaderContext.getContext().getHeaders();

        //logger.info("header is " + headers);
        for (Map.Entry<String, String> entry : headers.entrySet()) {
            conn.addRequestProperty(entry.getKey(), entry.getValue());
        }

    }


    @Override
    public URLConnection openConnection(URL url) throws IOException {
        URLConnection conn = super.openConnection(url);


        //此处 处理header
        handlHeader(conn);

        // modified by kz-tu 20131203 支持Hessian的https远程调用
        if (conn instanceof HttpsURLConnection) {
            HttpsURLConnection conn1 = (HttpsURLConnection) conn;

//            logger.info("[openConnection]URLConnection is HttpsURLConnection, Url:" + url);
                    ((HttpsURLConnection) conn).setHostnameVerifier(hostnameVerifier);
        }else{
//            logger.info("[openConnection]URLConnection is HttpURLConnection, Url:" + url);
        }

        conn.setDoOutput(true);
        conn.setRequestProperty("Token-Key", SendHessianKey.getInstance());

        if (this.connectTimeOut > 0) {
            conn.setConnectTimeout(this.connectTimeOut);
            /*if (logger.isDebugEnabled()) {
                logger.debug("Set Hessian Connection Timeout :" + this.connectTimeOut);
            }*/
        }

        if (this.readTimeOut > 0) {
            conn.setReadTimeout(this.readTimeOut);
            /*if (logger.isDebugEnabled()) {
                logger.debug("Set Hessian Read Timeout :" + this.readTimeOut);
            }*/
        }

        return conn;
    }

    public int getConnectTimeOut() {
        return connectTimeOut;
    }

    public void setConnectTimeOut(int connectTimeOut) {
        this.connectTimeOut = connectTimeOut;
    }

    public int getReadTimeOut() {
        return readTimeOut;
    }

    public void setReadTimeOut(int readTimeOut) {
        this.readTimeOut = readTimeOut;
    }
}
